{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "36df917d",
   "metadata": {},
   "source": [
    "# Quality Factors\n",
    "## Resonant Circuits and Q-factor\n",
    "Resonant circuits are used extensively in oscillators, tuned amplifiers, filters, etc. At a particular frequency, _a resonant frequency_ $f_r$ (or $\\omega_r$), resonators presents a maximum (or a minimum) impedance (ex: open or short circuit)[1]. The quality factor $Q$, or Q-factor, is a unitless figure of merit of the losses of a resonant passive circuit, defined as [2,3]:\n",
    "\n",
    "$$\n",
    "Q = 2 \\pi \\left. \\frac{\\textrm{Average Energy Stored}}{\\textrm{Energy Loss per Second}} \\right|_{\\omega=\\omega_r}\n",
    "=\n",
    "\\omega_r \\left. \\frac{\\textrm{Average Energy Stored}}{\\textrm{Average Power Loss}} \\right|_{\\omega=\\omega_r}\n",
    "$$\n",
    "\n",
    "From this definition, lower loss implies a higher $Q$. Resonators having higher Q-factors resonate with greater amplitudes (at the resonant frequency) but have a smaller range of frequencies $BW$ around that frequency for which they resonate.\n",
    "\n",
    "\n",
    "## Loaded of Unloaded Q-factors\n",
    "Actually, there are three Q factors than can be defined depending upon which loss is being considered [2]:\n",
    "\n",
    "* Unloaded Q: $$Q_0 = \\omega_r \\left. \\frac{\\textrm{Energy Stored in the Resonant Circuit}}{\\textrm{Power Loss in the Resonant Circuit}} \\right|_{\\omega=\\omega_r}$$\n",
    "\n",
    "* External Q: $$Q_e = \\omega_r \\left. \\frac{\\textrm{Energy Stored in the Resonant Circuit}}{\\textrm{Power Loss in the External Circuit}} \\right|_{\\omega=\\omega_r}$$\n",
    "\n",
    "* Loaded Q: $$Q_L = \\omega_r \\left. \\frac{\\textrm{Energy Stored in the Resonant Circuit}}{\\textrm{Total Power Loss}} \\right|_{\\omega=\\omega_r}$$\n",
    "\n",
    "The _loaded_ Q-factor $Q_L$, describes energy dissipation within the entire resonant system comprising of the resonator itself and the instrument used for observing resonances [3]. The term _loading_ refers to the effect that the external circuit has on measured quantities. Any loss mechanisms due to the external circuitry will have the effect of lowering the $Q$.\n",
    "\n",
    "The _unloaded_ Q-factor $Q_0$ is a characteristic of the resonator itself, in the absence of any loading effects caused by external circuitry (uncoupled). For most applications, the quantity that is desired is the unloaded Q-factor $Q_0$ which is determined by energy dissipation associated with the resonator only and therefore gives the best description of the resonant mode. Direct measurement of the unloaded Q of a resonator is generally not possible because of the loading effect of the measurement system. However, it is possible to estimate $Q_0$ from measurements of the frequency response of the loaded resonator.\n",
    "\n",
    "The energy dissipation in the external circuit is characterized by the _external_ Q-factor $Q_e$ and these three Q-factors are related by the relationship (deduced from the three expressions above):\n",
    "\n",
    "$$\n",
    "\\frac{1}{Q_L} = \\frac{1}{Q_e} + \\frac{1}{Q_0} \n",
    "$$\n",
    "\n",
    "If one defines the _coupling factor_ $\\beta=Q_0/Q_e$ then:\n",
    "\n",
    "$$\n",
    "Q_0 = (1 + \\beta) Q_L\n",
    "$$\n",
    "\n",
    "Three cases can be distinguished:\n",
    "\n",
    "1. $\\beta<1$: The resonator is said to be _undercoupled_ to the feeding circuitry\n",
    "2. $\\beta=1$: The resonator is said to be _critically coupled_\n",
    "3. $\\beta>1$: The resonator is said to be _overcoupled_\n",
    "\n",
    "\n",
    "While the measurements of the loaded Quality factor $Q_L$ is straightforward, obtaining uncertainty below 1% (which is considered to be low for Q-factor measurement) requires attention to several aspects of the experimental procedure. In addition, finding the unloaded $Q_0$ from measured S-parameters consists in first finding the coupling factor $\\beta$, then measure $Q_L$ from the 3dB bandwidth and using the relationships above.\n",
    "\n",
    "Fortunately, `scikit-rf` implements automatic methods for determining _loaded_ and _unloaded_ Q-factors from frequency-domain S-parameters. The implemented methods are described in detail in [4], and can be applied to measurements of transmission or reflection.\n",
    "\n",
    "## Example with a Parallel RLC Circuit\n",
    "To illustrate the usage of the `Qfactor` class of `scikit-rf`, a parallel RLC circuit as illustrated above is used as an canonical example for which analytical formulas are available for benchmarking.\n",
    "\n",
    "<img src=\"figures/Parallel_RLC_resonator.svg\" width=\"300\"/>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b361222e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "import skrf as rf\n",
    "\n",
    "rf.stylely()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1735918b",
   "metadata": {},
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb7eba12",
   "metadata": {},
   "outputs": [],
   "source": [
    "C = 1e-6  # F\n",
    "L = 1e-9  # H\n",
    "R = 30  # Ohm\n",
    "Z0 = 50  # Ohm\n",
    "\n",
    "freq = rf.Frequency(5, 5.2, npoints=501, unit='MHz')\n",
    "media = rf.DefinedGammaZ0(frequency=freq, z0=Z0)  # ideal line (no loss)\n",
    "rng = np.random.default_rng()\n",
    "random_d = rng.uniform(-np.pi, np.pi)  # a random length for the sake of the example\n",
    "\n",
    "resonator = media.line(d=random_d, unit='rad') \\\n",
    "                **media.shunt_inductor(L) ** media.shunt_capacitor(C) \\\n",
    "                ** media.shunt(media.resistor(R)**media.short()) ** media.open()\n",
    "\n",
    "resonator.plot_s_db()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b291cfb1",
   "metadata": {},
   "source": [
    "Analytical formulas for this case are available and given by [2,3]:\n",
    "$$ f_{\\mathrm{res}} = \\frac{1}{2\\pi \\sqrt{L C}} $$\n",
    "$$ Q = \\omega_r R C = \\frac{R C}{\\sqrt{L C}}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c099dff",
   "metadata": {},
   "outputs": [],
   "source": [
    "def f_res_RLC(L, C):\n",
    "    return 1/(2*np.pi*np.sqrt(L*C))\n",
    "\n",
    "def Q_RLC(R, L, C):\n",
    "    return R * C / np.sqrt(L*C)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99d749a3",
   "metadata": {},
   "source": [
    "The unloaded Q-factor $Q_0$ is a characteristic of the resonator itself, in the absence of any loading effects caused by the external circuitry. In practice, of course, the resonator is connected and coupled to this circuitry which have the effect to lower the Q-factor of the loaded circuit $Q_L$. \n",
    "\n",
    "Hence, when the resonator is connected to the transmission line, it is loaded with the port impedance $Z_0$ (assuming no loss in the transmission line), which is connected in parallel to the intrinsic resonator resistor. The equivalent resonator load is therefore $R \\parallel Z_0 = (R Z_0) / (R+Z_0)$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c2b5cec7",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'Theoretical Resonant Frequency: {f_res_RLC(L, C)/1e6} MHz')\n",
    "print(f'Theoretical Loaded Q: Q_L = {Q_RLC((R*Z0)/(R+Z0), L, C)}')  # Req = R//Z0\n",
    "print(f'Theoretical Unloaded Q: Q_0 = {Q_RLC(R, L, C)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d84608c3",
   "metadata": {},
   "source": [
    "First, let's create a `Qfactor` object, passing the resonator Network and the type of resonator we are dealing with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cde6c0c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "Q = rf.Qfactor(resonator, res_type='reflection')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f246179",
   "metadata": {},
   "source": [
    "Note that in case of multiple resonances ([like in this example](../examples/qfactor/Finding_Dk_Df_from_resonance_fitting.ipynb)), it is recommended to also pass the estimated resonance frequency and eventually the estimated (order of magnitude) Q-factor.  \n",
    "\n",
    "Then, we use the `fit` method to fit the loaded Q-factor $Q_L$ and resonant frequency. The returned results will be useful to deduce the unloaded Q-factor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "226c8348",
   "metadata": {},
   "outputs": [],
   "source": [
    "res = Q.fit()\n",
    "print(f'Fitted Resonant Frequency: f_L = {Q.f_L/1e6} MHz')\n",
    "print(f'Fitted Loaded Q-factor: Q_L = {Q.Q_L}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "354b8a6f",
   "metadata": {},
   "source": [
    "Note that these results are also shown if you print the `Qfactor` object: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dad8209e",
   "metadata": {},
   "outputs": [],
   "source": [
    "Q"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fbfe3c69",
   "metadata": {},
   "source": [
    "Finally, the unloaded Q-factor is deduced from the fitting properties: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2cd74007",
   "metadata": {},
   "outputs": [],
   "source": [
    "Q0 = Q.Q_unloaded(res)\n",
    "print(f'Fitted Unloaded Q-factor: Q_0 = {Q0}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39402d6e",
   "metadata": {},
   "source": [
    "Note that passing the result is optional. Calling the `.fit()` method will store internally the optimized results and use it if a specific optimized result is not passed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1996ce20",
   "metadata": {},
   "outputs": [],
   "source": [
    "Q0 = Q.Q_unloaded()  # will use the latest optimized results performed with .fit()\n",
    "print(f'Fitted Unloaded Q-factor: Q_0 = {Q0}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97256f69",
   "metadata": {},
   "source": [
    "Note that the analytical results and the fitted ones match well, the relative error is small:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "209c661f",
   "metadata": {},
   "outputs": [],
   "source": [
    "print('Relative Error on Q_0:', (Q_RLC(R, L, C) - Q0)/Q_RLC(R, L, C))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0aab0a5e",
   "metadata": {},
   "source": [
    "It is also possible to generate a Network from the fitted results. For example, we can compare the fitted resonator model for more frequency points to the initial RLC resonator: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63f2e0f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "new_freq = rf.Frequency(5, 5.2, npoints=5001, unit='MHz')\n",
    "fitted_network = Q.fitted_network(res, frequency=new_freq)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3d3f4d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "resonator.plot_s_mag(label='Parallel RLC ', lw=2)\n",
    "fitted_network.plot_s_mag(label='Fitted Model', lw=2, ls='--')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc1a11d7",
   "metadata": {},
   "source": [
    "## Q-Circle\n",
    "Close to a resonance, a resonator can be represented by an equivalent circuit model [1,4]. \n",
    "\n",
    "The Scattering-parameters (reflection or transmission) of RF circuits including resonator(s) have the form of circles in the complex plane $(\\Re(s), \\Im(s))$. S-parameters can be expressed as a function of the frequency $f$, the loaded quality factor $Q_L$ [1,4]:\n",
    "\n",
    "$$\n",
    "S(f) = S_D + d \\frac{e^{-2 j \\delta}}{1 + j Q_L \\mathcal{F}}\n",
    "$$\n",
    "\n",
    "where :\n",
    "- $S_D$ is the detuned S-parameter measured at frequencies far above or below the resonance\n",
    "- $Q_L$ is the loaded Q-factor\n",
    "- $f_L$ is the loaded resonant frequency\n",
    "- $f_0$ is the unloaded resonant frequency\n",
    "- $d$ is the diameter of the Q-circle\n",
    "- $\\delta$ is a real valued constant that defines the orientation of the Q-circle\n",
    "- $\\mathcal{F}$ is the fractional offset frequency given by:\n",
    "$$\n",
    "\\mathcal{F} = \\frac{f}{f_L} - \\frac{f_L}{f}\n",
    "$$\n",
    "\n",
    "The fractional frequency $\\mathcal{F}$ is a convenient way to express the frequency when dealing with resonant circuits:  $\\mathcal{F}=0$ at the resonant frequency and when $f<f_L$, $\\mathcal{F}$ is negative, while it is positive when $f>f_L$. If $\\Delta f = f - f_L$ is the frequency deviation from the resonance, then close to the resonance:\n",
    "\n",
    "$$\\mathcal{F} \\approx 2 \\frac{\\Delta f}{f_L}$$\n",
    "\n",
    "For example, if the source frequency is 10% below the resonant frequency ($0.9 f/f_L$), then $\\mathcal{F}\\approx -0.2$."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef03d8d7",
   "metadata": {},
   "source": [
    "The parameters (diameters and tuned and off-tuned S-parameters) of the Q-circle can eventually be obtained from: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e62a21ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "diam, S_V, S_T = Q.Q_circle()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e7a62a1",
   "metadata": {},
   "source": [
    "Use a polar plane to check the results: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb1e2ce2",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})\n",
    "resonator.plot_s_polar(ax=ax, label=\"RLC Resonator\", ls='', marker='x', ms=5)\n",
    "fitted_network.plot_s_polar(ax=ax, label=\"Fitted Model\", lw=2)\n",
    "ax.plot(np.angle(S_V), np.abs(S_V), 'ko')\n",
    "ax.plot(np.angle(S_T), np.abs(S_T), 'ko')\n",
    "ax.text(np.angle(S_T), 0.8*np.abs(S_T), '$S_T$')\n",
    "ax.text(np.angle(S_V), 1.1*np.abs(S_V), '$S_V$')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3441f1f9",
   "metadata": {},
   "source": [
    "## Bandwidth\n",
    "Another definition of the Q-factor is the frequency-to-bandwidth ratio of a transmission resonator [6]:\n",
    "\n",
    "$$\n",
    "Q_L = \\frac{\\omega_r}{\\Delta \\omega} = \\frac{f_r}{\\Delta f} \n",
    "$$\n",
    "\n",
    "where $f_r$ is the resonant frequency, $\\Delta f=BW$ is the resonance width or  _fractional bandwidth_ BW. Note that this definition is only an approximation, which can be not accurate when there is significant leakage (see [4] for more information). The definition of the bandwidth also depends on the resonator type:\n",
    "\n",
    "* transmission:  bandwidth, also known as _full width at half maximum_ (FWHM), is the bandwidth over which the dissipated power is half the maximum value at the resonant frequency (3 dB below the max value).\n",
    "* reflection: while it is possible to define the bandwidth as the 3-dB points below the max reflection coefficient [6], a more proper definition of the bandwidth consists in measuring the difference between frequencies $f_1$ and $f_2$, which belong to the two points inclined by 45 degrees on each side of the centerline from $S_T$ to the origin (see [4,8] for additional details).\n",
    "\n",
    "Using the previous relation, the bandwidth BW can also be obtained from the `Qfactor` class via the `.BW` parameter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c3217598",
   "metadata": {},
   "outputs": [],
   "source": [
    "BW = Q.BW\n",
    "print(f'Bandwidth: {BW} Hz')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "240d7ea3",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = plt.subplots()\n",
    "resonator.plot_s_db(label='Parallel RLC ', lw=2, ax=ax)\n",
    "ax.axvspan(xmin=Q.f_L-Q.BW/2, xmax=Q.f_L+Q.BW/2, alpha=0.3, label='Bandwidth')\n",
    "ax.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ffd32f31",
   "metadata": {},
   "source": [
    "## References\n",
    "- [1] B. A. Galwas, \"Scattering Matrix Description of Microwave Resonators,\" in IEEE Transactions on Microwave Theory and Techniques, vol. 31, no. 8, pp. 669-671, Aug. 1983, doi: 10.1109/TMTT.1983.1131566.\n",
    "- [2] Peter A. Rizzi, \"Microwave Engineering: Passive Circuits\", Prentice-Hall, 1988 \n",
    "- [3] David M. Pozar, \"Microwave Engineering\", 4th Edition, Éditeur\tWiley, 2011\n",
    "- [4] A. P. Gregory, \"Q-factor Measurement by using a Vector Network Analyser\", National Physical Laboratory Report MAT 58 (2021), https://eprintspublications.npl.co.uk/9304/\n",
    "- [5] Microwaves101, \"Resonance of RLC Circuits\", https://www.microwaves101.com/encyclopedias/resonance-of-rlc-circuits\n",
    "- [6] Green, Estill I. (October 1955). \"The Story of Q\", American Scientist. 43: 584–594. http://www.collinsaudio.com/Prosound_Workshop/The_story_of_Q.pdf\n",
    "- [7] R. S. Kwok and J.-F. Liang, ‘Characterization of high-Q resonators for microwave filter applications’, IEEE Transactions on Microwave Theory and Techniques, vol. 47, no. 1, pp. 111–114, Jan. 1999, doi: 10.1109/22.740093.\n",
    "- [8] Darko Kajfez, \"Q factor measurements, analog and digital\", https://people.engineering.olemiss.edu/darko-kajfez/assets/rfqmeas2b.pdf\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffeecab3",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
